#pragma once
#include "file.h"
#include "task.h"
#include <arpa/inet.h>
#include <cstdint>
#include <cstring>
#include <iostream>
#include <netdb.h>
#include <netinet/in.h>
#include <ostream>
#include <string_view>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <variant>

namespace cobt {
class SocketAddress {
public:
  friend class SocketServer;
  SocketAddress() = default;
  SocketAddress(const SocketAddress &addr) = default;
  SocketAddress(const sockaddr *addr, socklen_t len) { set_addr(addr, len); }
  SocketAddress(std::string_view ip, uint16_t port) {
    m_port = port;
    std::string port_str = std::to_string(port);
    resolve_hostname(ip.data(), port_str.c_str());
  }
  bool operator==(const SocketAddress &addr) const {
    if (std::holds_alternative<sockaddr_in>(m_addr) &&
        std::holds_alternative<sockaddr_in>(addr.m_addr)) {
      auto this_addr = std::get<sockaddr_in>(m_addr);
      auto that_addr = std::get<sockaddr_in>(addr.m_addr);
      return this_addr.sin_addr.s_addr == that_addr.sin_addr.s_addr &&
             this_addr.sin_port == that_addr.sin_port;
    }
    if (std::holds_alternative<sockaddr_in6>(m_addr) &&
        std::holds_alternative<sockaddr_in6>(addr.m_addr)) {
      auto this_addr = std::get<sockaddr_in6>(m_addr);
      auto that_addr = std::get<sockaddr_in6>(addr.m_addr);
      return (std::memcmp(&this_addr, &that_addr, sizeof(struct in6_addr)) ==
              0) &&
             this_addr.sin6_port == that_addr.sin6_port;
    }
    return false;
  }
  void resolve_hostname(const char *hostname, const char *service) {
    struct addrinfo hints, *res, *p;
    int status;

    // 清零 hints 结构体
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_UNSPEC; // 不指定 IPv4 或 IPv6
    hints.ai_socktype = SOCK_STREAM;

    // 解析主机名
    if ((status = getaddrinfo(hostname, service, &hints, &res)) != 0) {
      std::cerr << "getaddrinfo of " << hostname << ':' << service << " : "
                << gai_strerror(status) << std::endl;
      return;
    }
    void *addr;
    const char *ipver;
    // 遍历结果并打印
    for (p = res; p != NULL; p = p->ai_next) {
      if (p->ai_family == AF_INET6) {
        m_addr = *(struct sockaddr_in6 *)p->ai_addr;
        addr = &std::get<sockaddr_in6>(m_addr).sin6_addr;
        m_addr_len = sizeof(struct sockaddr_in6);
        break;
      } else if (p->ai_family == AF_INET) { // IPv4
        m_addr = *(struct sockaddr_in *)p->ai_addr;
        addr = &std::get<sockaddr_in>(m_addr).sin_addr;
        m_addr_len = sizeof(struct sockaddr_in);
        memset(&m_addr6, 0, sizeof(m_addr6));
        m_addr6.sin6_family = AF_INET6;
        m_addr6.sin6_addr.s6_addr[10] = 0xff;
        m_addr6.sin6_addr.s6_addr[11] = 0xff;
        memcpy(&m_addr6.sin6_addr.s6_addr[12], &addr,
               sizeof(sockaddr_in::sin_addr));
        m_addr6.sin6_port = htons(m_port);
        break;
      }
    }
    // 将 IP 地址转换为字符串并打印
    inet_ntop(p->ai_family, addr, m_ipstr.data(), INET6_ADDRSTRLEN);
    freeaddrinfo(res); // 释放 addrinfo 结构体
  }
  sa_family_t get_family() const {
    if (std::holds_alternative<sockaddr_in>(m_addr)) {
      return AF_INET;
    }
    return AF_INET6;
  };

  socklen_t get_addr_len() const { return m_addr_len; }
  socklen_t &get_addr_len() { return m_addr_len; }
  uint16_t get_port() { return m_port; }
  const sockaddr_in6 *get_addr6() const {
    if (std::holds_alternative<sockaddr_in6>(m_addr)) {
      return &std::get<sockaddr_in6>(m_addr);
    }

    return &m_addr6;
  }
  const sockaddr_in *get_addr4() const {
    return &std::get<sockaddr_in>(m_addr);
  }
  sockaddr *get_addr() {
    if (std::holds_alternative<sockaddr_in>(m_addr)) {
      return reinterpret_cast<sockaddr *>(&std::get<sockaddr_in>(m_addr));
    }
    return reinterpret_cast<sockaddr *>(&std::get<sockaddr_in6>(m_addr));
  }
  const sockaddr *get_addr() const {
    if (std::holds_alternative<sockaddr_in>(m_addr)) {
      return reinterpret_cast<const sockaddr *>(&std::get<sockaddr_in>(m_addr));
    }
    return reinterpret_cast<const sockaddr *>(&std::get<sockaddr_in6>(m_addr));
  }

  void set_port(uint16_t port) {
    m_port = port;
    switch (m_addr.index()) {
    case 0:
      std::get<sockaddr_in>(m_addr).sin_port = htons(m_port);
      break;
    case 1:
      std::get<sockaddr_in6>(m_addr).sin6_port = htons(m_port);
      break;
    }
  }
  void set_addr(const sockaddr *addr, socklen_t len) {
    m_addr_len = len;
    if (addr->sa_family == AF_INET) {
      m_addr = *(struct sockaddr_in *)addr;
      m_port = ntohs(std::get<sockaddr_in>(m_addr).sin_port);
      inet_ntop(AF_INET, &std::get<sockaddr_in>(m_addr).sin_addr,
                m_ipstr.data(), INET6_ADDRSTRLEN);
      memset(&m_addr6, 0, sizeof(m_addr6));
      m_addr6.sin6_family = AF_INET6;
      m_addr6.sin6_addr.s6_addr[10] = 0xff;
      m_addr6.sin6_addr.s6_addr[11] = 0xff;
      memcpy(&m_addr6.sin6_addr.s6_addr[12],
             &std::get<sockaddr_in>(m_addr).sin_addr,
             sizeof(sockaddr_in::sin_addr));
      m_addr6.sin6_port = htons(m_port);
    } else {
      m_addr = *(struct sockaddr_in6 *)addr;
      m_port = ntohs(std::get<sockaddr_in6>(m_addr).sin6_port);
      inet_ntop(AF_INET6, &std::get<sockaddr_in6>(m_addr).sin6_addr,
                m_ipstr.data(), INET6_ADDRSTRLEN);
    }
  }
  const char *get_ipstr() const { return m_ipstr.data(); }
  uint16_t get_port() const { return m_port; }

  std::ostream &operator<<(std::ostream &os) const {
    os << m_ipstr << ' ' << m_port;
    return os;
  }

private:
  std::variant<sockaddr_in, sockaddr_in6> m_addr;
  sockaddr_in6 m_addr6;
  socklen_t m_addr_len;
  uint16_t m_port;
  std::string m_ipstr = std::string(INET6_ADDRSTRLEN, 0);
};

inline std::ostream &operator<<(std::ostream &os, const SocketAddress &addr) {
  return os << addr.get_ipstr() << ' ' << addr.get_port();
}

struct SocketPromise : cobt::Promise<void> {
  auto get_return_object() {
    return std::coroutine_handle<SocketPromise>::from_promise(*this);
  }
  void unhandled_exception() {
    m_exception = std::current_exception();
    std::rethrow_exception(m_exception);
  }
  UnInit<cobt::FileHandle> m_conn;
};
} // namespace cobt